#!/bin/sh

###################################################################################################
# THIS MODULE INCLUDES ALL ACTIONS INTENDED TO ISOLATE DOTFILES OR CONTAINERIZE INSTALLED APPIMAGES
###################################################################################################

AMCLIPATH_ORIGIN="$AMCLIPATH"
SUDOCMD_ORIGIN="$SUDOCMD"

# Get xdg variables for _configure_dirs_access
for DIR in DESKTOP DOCUMENTS DOWNLOAD GAMES MUSIC PICTURES VIDEOS; do
	eval XDG_DIR="$(xdg-user-dir $DIR 2>/dev/null)"
	[ "$(readlink -f "$XDG_DIR")" = "$HOME" ] && XDG_DIR=""
	eval $DIR="$XDG_DIR"
done
DESKTOP="$(echo "${DESKTOP:-~/Desktop}" | sed "s|$HOME|~|g")"
DOCUMENTS="$(echo "${DOCUMENTS:-~/Documents}" | sed "s|$HOME|~|g")"
DOWNLOAD="$(echo "${DOWNLOAD:-~/Downloads}" | sed "s|$HOME|~|g")"
GAMES="$(echo "${GAMES:-~/Games}" | sed "s|$HOME|~|g")"
MUSIC="$(echo "${MUSIC:-~/Music}" | sed "s|$HOME|~|g")"
PICTURES="$(echo "${PICTURES:-~/Pictures}" | sed "s|$HOME|~|g")"
VIDEOS="$(echo "${VIDEOS:-~/Videos}" | sed "s|$HOME|~|g")"

_check_appimage() {
	_determine_args
	TARGET="$(command -v "$1" 2>/dev/null)"
	APPIMAGE="$(readlink "$TARGET" 2>/dev/null)"
	APPIMAGEDIR="$(dirname "$APPIMAGE" 2>/dev/null)"
	if grep "aisap-am" "$TARGET" >/dev/null 2>&1; then
		printf $"%b\n \"%b\" is already sandboxed!\n%b\n" "$DIVIDING_LINE" "$1" "$DIVIDING_LINE"
		return 1
	elif [ ! -e "$APPIMAGE" ]; then
		printf $"%b\n ERROR: \"%b\" is not installed\n%b\n" "$DIVIDING_LINE" "$1" "$DIVIDING_LINE"
		return 1
	elif ! head -c10 "$APPIMAGE" 2>/dev/null | grep -qa '^.ELF....AI$'; then
		printf $"%b\n ERROR: \"%b\" is NOT an AppImage\n%b\n" "$DIVIDING_LINE" "$1" "$DIVIDING_LINE"
		return 1
	fi
}

_disable_sandbox() {
	TARGET="$(command -v "$1")"
	if ! grep "aisap-am sandboxing script" "$TARGET" >/dev/null 2>&1; then
		echo $" ERROR: Not a sandboxed AppImage, aborting"
		return 1
	fi
	"$1" --disable-sandbox
}

_check_aisap() {
	if [ "$1" = "aisap" ] || [ "$1" = "sas" ]; then
		echo $" Error: You can't sandbox $1"
		return 1
	elif ! command -v aisap 1>/dev/null && ! command -v sas 1>/dev/null; then
		printf $'\n%s\n' " ERROR: You need aisap or sas for this script to work"
		printf $'\n%s\n\n' " NOTE: Only sas can sandbox DWARFS AppImages"
		read -r -p $" ◆ DO YOU WISH TO INSTALL SAS? Install size <5 MiB? (Y/n) " yn
		case "$yn" in
			n*|N*) echo $" OPERATION ABORTED!"; return 1;;
		esac
		if [ "$CLI" = am ] && [ -f "$APPMANCONFIG"/appman-config ]; then
			read -r -p $" ◆ DO YOU WISH TO INSTALL SAS LOCALLY? (Y/n) " yn
			case "$yn" in
				n*|N*) "$AMCLIPATH_ORIGIN" -i sas >/dev/null 2>&1;;
				''|*)  "$AMCLIPATH_ORIGIN" -i --user sas >/dev/null 2>&1;;
			esac
		else
			"$AMCLIPATH_ORIGIN" -i sas >/dev/null 2>&1
		fi
		command -v sas 1>/dev/null || return 1
		echo $" sas installed successfully!"
	fi
	if [ -f "$BINDIR"/"$1" ]; then
		SUDOCMD=""
	fi
}

_generate_sandbox_script() {
	echo "$DIVIDING_LINE"
	printf '\n%s\n' " Making aisap sandbox script for \"$1\"..."
	tmpscript="$(cat <<-'HEREDOC'
	#!/bin/sh
	# aisap-am sandboxing script, aisap: https://github.com/mgord9518/aisap
	# Thanks a lot to mgord9518 for making aisap!
	# Run this script with --disable-sandbox to do what the flag name implies
	# The default location for the sandboxed home is in $HOME/.local/am-sandboxes
	# But that location can be changed by setting the $SANDBOXDIR env variable

	# Set common XDG variables
	BINDIR="${XDG_BIN_HOME:-$HOME/.local/bin}"
	DATADIR="${XDG_DATA_HOME:-$HOME/.local/share}"
	CONFIGDIR="${XDG_CONFIG_HOME:-$HOME/.config}"
	CACHEDIR="${XDG_CACHE_HOME:-$HOME/.cache}"

	# Dependency check
	if command -v sas 1>/dev/null; then
	  SANDBOXCMD="sas"
	elif command -v aisap 1>/dev/null; then
	  SANDBOXCMD="aisap"
	elif [ -f "$BINDIR/sas" ]; then
	  SANDBOXCMD="$BINDIR/sas"
	elif [ -f "$BINDIR/aisap" ]; then
	  SANDBOXCMD="$BINDIR/aisap"
	else
	  echo "You need aisap or sas for this to work"
	  notify-send -u critical "Sandbox error: Missing aisap/sas dependency!"
	  exit 1
	fi

	# Set variables and create sandboxed dir
	APPEXEC=DUMMY
	chmod a-x "$APPEXEC" # Prevents accidental launch of app outside the sandbox
	APPNAME="$(echo "$APPEXEC" | awk -F "/" '{print $NF}')"
	SANDBOXDIR="${SANDBOXDIR:-$HOME/.local/am-sandboxes}"
	DBUS="$(ls /tmp/dbus* 2>/dev/null | head -1)"

	# get xdg user dirs, unset if var = $HOME
	for DIR in DESKTOP DOCUMENTS DOWNLOAD GAMES MUSIC PICTURES VIDEOS; do
	  eval XDG_DIR="$(xdg-user-dir $DIR 2>/dev/null)"
	  [ "$(readlink -f "$XDG_DIR")" = "$HOME" ] && XDG_DIR=""
	  eval $DIR="$XDG_DIR"
	done
	# Use default location if var is not set
	DESKTOP="${DESKTOP:-$HOME/Desktop}"
	DOCUMENTS="${DOCUMENTS:-$HOME/Documents}"
	DOWNLOAD="${DOWNLOAD:-$HOME/Downloads}"
	GAMES="${GAMES:-$HOME/Games}"
	MUSIC="${MUSIC:-$HOME/Music}"
	PICTURES="${PICTURES:-$HOME/Pictures}"
	VIDEOS="${VIDEOS:-$HOME/Videos}"
	# Try find the right name of the app conf/data dir
	APPDATA=$( ls "$DATADIR" | grep -i "$APPNAME" | head -1 )
	APPCONF=$( ls "$CONFIGDIR" | grep -i "$APPNAME" | head -1 )
	# Disable sandbox
	if [ "$1" = "--disable-sandbox" ]; then
	  APPIMAGEDIR="${APPEXEC%/*}"
	  echo ""
	  echo "✔ Giving exec permissions back to $APPEXEC..." \
	    | fold -sw 77 | sed 's/^/   /g; s/   ✔/ ✔/g'
	  chmod a+x "$APPEXEC" || exit 1
	  echo "✔ Patching $APPIMAGEDIR/AM-updater to give permissions back..." \
	    | fold -sw 77 | sed 's/^/   /g; s/   ✔/ ✔/g'
	  tmpsedEEE="$(sed 's|chmod a-x|chmod a+x|g' "$APPIMAGEDIR/AM-updater" 2>/dev/null)"
	  [ -n "$tmpsedEEE" ] && echo "$tmpsedEEE" > "$APPIMAGEDIR/AM-updater" || return 1
	  unset tmpsedEEE
	  THISFILE="$(readlink -f "$0")"
	  echo "✔ Replacing $THISFILE with a link to the AppImage..." \
	    | fold -sw 77 | sed 's/^/   /g; s/   ✔/ ✔/g'
	  SUDO ln -sf "$APPEXEC" "$THISFILE" || exit 1
	  printf '\033[32m\n%s\n\033[0m\n' "✔ $APPEXEC successfully unsandboxed!" \
	    | fold -sw 77 | sed 's/^/   /g; s/   ✔/ ✔/g'
	  exit 0
	fi
	mkdir -p "$SANDBOXDIR/$APPNAME"
	[ -z "$APPNAME" ] && exit 1
	# Start at sandboxed home
	# Edit below this to add or remove access to parts of the system
	exec "$SANDBOXCMD" --trust-once --level 1 \
	--data-dir "$SANDBOXDIR/$APPNAME" \
	--add-file "$DATADIR/${APPDATA:-$APPNAME}":rw \
	--add-file "$DATADIR"/themes \
	--add-file "$DATADIR"/icons \
	--add-file "$DATADIR"/fonts \
	--add-file "$CONFIGDIR/${APPCONF:-$APPNAME}":rw \
	--add-file "$CONFIGDIR"/dconf \
	--add-file "$CONFIGDIR"/gtk3.0 \
	--add-file "$CONFIGDIR"/gtk4.0 \
	--add-file "$CONFIGDIR"/kdeglobals \
	--add-file "$CONFIGDIR"/qt5ct \
	--add-file "$CONFIGDIR"/qt6ct \
	--add-file "$CONFIGDIR"/Kvantum \
	--add-file "$HOME"/.local/lib \
	--rm-file /NOPATH \
	--rm-file "$DESKTOP" \
	--rm-file "$DOCUMENTS" \
	--rm-file "$DOWNLOAD" \
	--rm-file "$GAMES" \
	--rm-file "$MUSIC" \
	--rm-file "$PICTURES" \
	--rm-file "$VIDEOS" \
	--add-file /var/lib/dbus \
	--add-file "${DBUS:-/tmp/dbus}" \
	--add-socket pulseaudio \
	--add-socket dbus \
	--add-socket network \
	--add-socket x11 \
	--add-socket wayland \
	--add-device dri -- \
	"$APPEXEC" "$@"
	HEREDOC
	)"
}

_configure_dirs_access() {
	printf '\033[33m\n'
	read -r -p $" Do you want configure access to directories? (Y/n): " yn
	if echo "$yn" | grep -i '^n' >/dev/null 2>&1; then
		return 0
	fi
	printf '\033[36m'
	for DIR in DESKTOP DOCUMENTS DOWNLOAD GAMES MUSIC PICTURES VIDEOS; do
		eval XDG_DIR=\$$DIR
		read -r -p $" Allow $1 access to \"$XDG_DIR\"? (y/N) " yn
		if echo "$yn" | grep -i '^y' >/dev/null 2>&1; then
			tmpscript=$(echo "$tmpscript" \
				| sed "s#--rm-file \"\$$DIR\"#--add-file \"\$$DIR\":rw#g" )
		fi
	done
	sleep 0.5
	printf '\033[31m'
	read -r -p $" Allow $1 access to a specific directory? (y/N) " yn
	if echo "$yn" | grep -i '^y' >/dev/null 2>&1; then
		echo $"  WARNING: Giving access to all of $HOME or / and similar is not safe"
		echo $"  Also aisap might not let $1 start when such paths are given"
		printf $'\033[33m%s\n' "  Type the path to the directory"
		read -r -p $"  Example: /media/external-drive or ~/Backups: " NEWDIR
		NEWDIR="$(readlink -f "$NEWDIR")"
		case "$NEWDIR" in
			"/"|"/home"|"$HOME"|"$DATADIR"|"$CONFIGDIR"|"$BINDIR")
				notify-send -u critical "DO YOU WANT THE FBI TO GET YA?"
				printf '\033[31m\n'
				read -r -p $"  SPOOKY LOCATION! ARE YOU SURE? IF SO TYPE \"YES\": " YES
				[ "$YES" != "YES" ] && echo $" That's not \"YES\", aborting" && return 1
				;;
			'')
				printf $'\033[31m\n%s\n\n' " No path given, aborting"
				return 1
				;;
		esac
		echo $"  Giving access to \"$NEWDIR\"..."
		tmpscript=$(echo "$tmpscript" \
			| sed "s#--rm-file /NOPATH#--add-file \"$NEWDIR\":rw#g")
	fi
	printf $'\n\033[32m%s\n' " User directories access configured successfully!"
}

_install_sandbox_script() {
	tmpscript=$(echo "$tmpscript" | sed "s#DUMMY#$APPIMAGE#g; s#SUDO#$SUDOCMD#g")
	# Remove exec permission from AppImage and its updater for better safety™
	chmod a-x "$APPIMAGE" || return 1
	tmpsedEEE="$(sed 's|chmod a+x|chmod a-x|g' "$APPIMAGEDIR/AM-updater" 2>/dev/null)"
	[ -n "$tmpsedEEE" ] && echo "$tmpsedEEE" > "$APPIMAGEDIR/AM-updater" || return 1
	unset tmpsedEEE
	# Install the script
	$SUDOCMD rm -f "$TARGET" || return 1
	echo "$tmpscript" | $SUDOCMD tee "$TARGET" >/dev/null 2>&1 || return 1
	$SUDOCMD chmod a+x "$TARGET"
	SANDBOXDIR="${SANDBOXDIR:-$HOME/.local/am-sandboxes}"
	printf $'\033[32m\n%s\n\033[0m' " \"$1\" successfully sandboxed!"
	printf $'\n%s\n' " $1 will be sandboxed in \"$SANDBOXDIR\""
	printf $'%s\n\n' " once launched"
	printf $'%s\n' " Set the \$SANDBOXDIR env variable to move the location"
	printf $'\n%s' ' Use '
	printf '\033[33m%s' '--disable-sandbox'
	printf $'\033[0m%s\033[33m\n' " to revert the changes, in this case that is:"
	printf '\033[33m%s\033[0m' " $1 --disable-sandbox"
	printf $'%s\033[33m%s\n\033[0m\n' " or " "$AMCLI --disable-sandbox $1"
	SUDOCMD="$SUDOCMD_ORIGIN"
}

# Main logic
[ -z "$2" ] && echo $" USAGE: $AMCLI $1 [ARGUMENT]" && exit 1
case "$1" in
	'--sandbox')
		shift
		while [ "$#" -gt 0 ]; do
			_check_appimage "${@}" && _check_aisap "${@}" \
			&& _generate_sandbox_script "${@}" \
			&& _configure_dirs_access "${@}" \
			&& _install_sandbox_script "${@}"
			shift
		done
	;;

	'--disable-sandbox')
		shift
		while [ "$#" -gt 0 ]; do
			echo "$DIVIDING_LINE"
			_disable_sandbox "${@}"
			shift
		done
		;;
esac
_remove_info_files
